#pragma once

#include <string>
#include <iostream>
#include <strings.h>
/*网络必要的头文件*/
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

/*其他操作系统接口*/
#include <unistd.h>
#include <pthread.h>
#include "blockQueue.hpp"
#include <vector>
using namespace blockqueue;

namespace client
{
    using namespace std;
    enum
    {
        SOCKET_ERROR = 1,
        START_ERROR
    };
    class udpClinet
    {
    public:
        udpClinet(const string &serverIp, const uint16_t &serverPort)
            : _socketFd(-1), _serverIp(serverIp), _serverPort(serverPort)
        {
            _socketFd = socket(AF_INET, SOCK_DGRAM, 0);
            if (_socketFd == -1)
            {
                cerr << "socket fail" << endl;
                exit(SOCKET_ERROR);
            }
            cout << "socket success: " << _socketFd << endl;
            /*客户端不关心自己的IP和端口号，所以不需要我们自己动手bind
             *但这并不代表客户端不需要bind，只是这个工作操作系统给做了*/
        }

        static void *productorDemo(void *args)
        {
            pthread_detach(pthread_self());
            udpClinet *_this = static_cast<udpClinet *>(args);
            blockQueue<string> *bq = _this->bq;
            while (true)
            {
                int socketFd = (static_cast<udpClinet *>(args))->_socketFd;
                char buffer[1024];
                struct sockaddr_in server;
                socklen_t len = sizeof(server);
                size_t n = recvfrom(socketFd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&server, &len);
                if(n > 0) buffer[n] = 0;
                string message = buffer;
                bq->push(message);
            }
        }

        void start()
        {
            /*主线程充当消费者，新线程充当生产者
             *生产者从网络当中获取消息，并将该消息写到阻塞队列当中
             *消费者直接从阻塞队列当中获取消息*/
            pthread_t consumer, productor;
            pthread_create(&productor, nullptr, productorDemo, this);

            while (true)
            {
                string message;
                cerr << "Please Say[回车刷新聊天记录]# ";
                getline(cin, message);
                struct sockaddr_in server = getSockaddr_in();
                sendto(_socketFd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));
                
                /*消费者从阻塞队列当中获取消息，一次全部拿完
                 *强制让主线程休眠一小会，目的是起到每次输入完后，强制调度生产者线程*/
                usleep(1234);
                while(!bq->isEmpty())
                {
                    string ret;
                    bq->pop(&ret);
                    cout << ret << endl;
                }
            }
        }

    private:
        /*网络通信三个条件：
         *网络文件、IP、端口号
         *作为客户端不需要关心自己的IP和端口号
         *要关心服务器的IP和端口号*/
        int _socketFd;
        string _serverIp;
        uint16_t _serverPort;

        static blockQueue<string> *bq;/*阻塞队列*/

        /*获取struct sockaddr结构体*/
        struct sockaddr_in getSockaddr_in()
        {
            struct sockaddr_in server;
            /*初始化sockaddr_in对象
             *并且将端口号、IP地址设置进去
             *我们使用的IP地址是字符串，所以需要转换成整数
             *使用inet_addr()接口，里面自动转换成整数，并且自动大小端字节序*/
            bzero((void *)&server, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port = htons(_serverPort);
            server.sin_addr.s_addr = inet_addr(_serverIp.c_str());
            return server;
        }
    };
    blockQueue<string> *udpClinet::bq = new blockQueue<string>();
} /*namespace client ends here*/